#ifndef _PFC_STRINGNEW_H_
#define _PFC_STRINGNEW_H_

namespace pfc
{
    //helper, const methods only
    class __stringEmpty : public string_base
    {
    public:
        const char * get_ptr() const
        {
            return "";
        }
        void add_string(const char * p_string,t_size p_length = ~0)
        {
            throw exception_not_implemented();
        }
        void set_string(const char * p_string,t_size p_length = ~0)
        {
            throw exception_not_implemented();
        }
        void truncate(t_size len)
        {
            throw exception_not_implemented();
        }
        t_size get_length() const
        {
            return 0;
        }
        char * lock_buffer(t_size p_requested_length)
        {
            throw exception_not_implemented();
        }
        void unlock_buffer()
        {
            throw exception_not_implemented();
        }
    };

    class stringp;

    //! New EXPERIMENTAL string class, allowing efficient copies and returning from functions. \n
    //! Does not implement the string_base interface so you still need string8 in many cases. \n
    //! Safe to pass between DLLs, but since a reference is used, objects possibly created by other DLLs must be released before owning DLLs are unloaded.
    class string
    {
    public:
        typedef rcptr_t<string_base const> t_data;
        typedef rcptr_t<string8> t_dataImpl;

        string() : m_content(rcnew_t<__stringEmpty>()) {}
        string(const char * p_source) : m_content(rcnew_t<string8>(p_source)) {}
        string(const char * p_source, t_size p_sourceLen) : m_content(rcnew_t<string8>(p_source,p_sourceLen)) {}
        string(t_data const & p_source) : m_content(p_source) {}
        template<typename TSource> string(const TSource & p_source);

        string const & toString() const
        {
            return *this;
        }

        //warning, not length-checked anymore!
        static string g_concatenateRaw(const char * item1, t_size len1, const char * item2, t_size len2)
        {
            t_dataImpl impl;
            impl.new_t();
            char * buffer = impl->lock_buffer(len1+len2);
            memcpy_t(buffer,item1,len1);
            memcpy_t(buffer+len1,item2,len2);
            impl->unlock_buffer();
            return string(t_data(impl));
        }

        string operator+(const string& p_item2) const
        {
            return g_concatenateRaw(ptr(),length(),p_item2.ptr(),p_item2.length());
        }
        string operator+(const char * p_item2) const
        {
            return g_concatenateRaw(ptr(),length(),p_item2,strlen(p_item2));
        }

        template<typename TSource> string operator+(const TSource & p_item2) const;

        template<typename TSource>
        const string & operator+=(const TSource & p_item)
        {
            *this = *this + p_item;
            return *this;
        }

        string subString(t_size base) const
        {
            if (base > length()) throw exception_overflow();
            return string(ptr() + base);
        }
        string subString(t_size base, t_size count) const
        {
            return string(ptr() + base,count);
        }

        string toLower() const
        {
            string8_fastalloc temp;
            temp.prealloc(128);
            stringToLowerAppend(temp,ptr(),~0);
            return string(temp.get_ptr());
        }
        string toUpper() const
        {
            string8_fastalloc temp;
            temp.prealloc(128);
            stringToUpperAppend(temp,ptr(),~0);
            return string(temp.get_ptr());
        }

        string clone() const
        {
            return string(ptr());
        }

        //!! @returns ~0 if not found.
        t_size indexOf(char c,t_size base = 0) const;
        //!! @returns ~0 if not found.
        t_size lastIndexOf(char c,t_size base = ~0) const;
        //!! @returns ~0 if not found.
        t_size indexOf(stringp s,t_size base = 0) const;
        //!! @returns ~0 if not found.
        t_size lastIndexOf(stringp s,t_size base = ~0) const;
        //!! @returns ~0 if not found.
        t_size indexOfAnyChar(stringp s,t_size base = 0) const;
        //!! @returns ~0 if not found.
        t_size lastIndexOfAnyChar(stringp s,t_size base = ~0) const;

        bool contains(char c) const;
        bool contains(stringp s) const;

        bool containsAnyChar(stringp s) const;

        bool startsWith(char c) const;
        bool startsWith(string s) const;
        bool endsWith(char c) const;
        bool endsWith(string s) const;

        char firstChar() const;
        char lastChar() const;

        string replace(stringp strOld, stringp strNew) const;

        static int g_compare(const string & p_item1, const string & p_item2)
        {
            return strcmp(p_item1.ptr(),p_item2.ptr());
        }
        bool operator==(const string& p_other) const
        {
            return g_compare(*this,p_other) == 0;
        }
        bool operator!=(const string& p_other) const
        {
            return g_compare(*this,p_other) != 0;
        }
        bool operator<(const string& p_other) const
        {
            return g_compare(*this,p_other) < 0;
        }
        bool operator>(const string& p_other) const
        {
            return g_compare(*this,p_other) > 0;
        }
        bool operator<=(const string& p_other) const
        {
            return g_compare(*this,p_other) <= 0;
        }
        bool operator>=(const string& p_other) const
        {
            return g_compare(*this,p_other) >= 0;
        }

        const char * ptr() const
        {
            return m_content->get_ptr();
        }
        const char * get_ptr() const
        {
            return m_content->get_ptr();
        }
        t_size length() const
        {
            return m_content->get_length();
        }
        t_size get_length() const
        {
            return m_content->get_length();
        }

        static bool isNonTextChar(char c)
        {
            return c >= 0 && c < 32;
        }

        char operator[](t_size p_index) const
        {
            PFC_ASSERT(p_index < length());
            return ptr()[p_index];
        }
        bool isEmpty() const
        {
            return length() == 0;
        }

        class comparatorCaseSensitive
        {
        public:
            template<typename T1,typename T2>
            static int compare(T1 const& v1, T2 const& v2)
            {
                return strcmp(stringToPtr(v1),stringToPtr(v2));
            }
            static int compare_ex(const char * v1, t_size l1, const char * v2, t_size l2)
            {
                return strcmp_ex(v1, l1, v2, l2);
            }

        };
        class comparatorCaseInsensitive
        {
        public:
            template<typename T1,typename T2>
            static int compare(T1 const& v1, T2 const& v2)
            {
                return stringCompareCaseInsensitive(stringToPtr(v1),stringToPtr(v2));
            }
        };
        class comparatorCaseInsensitiveASCII
        {
        public:
            template<typename T1,typename T2>
            static int compare(T1 const& v1, T2 const& v2)
            {
                return stricmp_ascii(stringToPtr(v1),stringToPtr(v2));
            }

            static int compare_ex(const char * v1, t_size l1, const char * v2, t_size l2)
            {
                return stricmp_ascii_ex(v1, l1, v2, l2);
            }
        };

        static bool g_equals(const string & p_item1, const string & p_item2)
        {
            return p_item1 == p_item2;
        }
        static bool g_equalsCaseInsensitive(const string & p_item1, const string & p_item2)
        {
            return comparatorCaseInsensitive::compare(p_item1,p_item2) == 0;
        }

        t_data _content() const
        {
            return m_content;
        }
    private:
        t_data m_content;
    };

    template<typename T> inline string toString(T const& val)
    {
        return val.toString();
    }
    template<> inline string toString(t_int64 const& val)
    {
        return format_int(val).get_ptr();
    }
    template<> inline string toString(t_int32 const& val)
    {
        return format_int(val).get_ptr();
    }
    template<> inline string toString(t_int16 const& val)
    {
        return format_int(val).get_ptr();
    }
    template<> inline string toString(t_uint64 const& val)
    {
        return format_uint(val).get_ptr();
    }
    template<> inline string toString(t_uint32 const& val)
    {
        return format_uint(val).get_ptr();
    }
    template<> inline string toString(t_uint16 const& val)
    {
        return format_uint(val).get_ptr();
    }
    template<> inline string toString(float const& val)
    {
        return format_float(val).get_ptr();
    }
    template<> inline string toString(double const& val)
    {
        return format_float(val).get_ptr();
    }
    template<> inline string toString(char const& val)
    {
        return string(&val,1);
    }
    inline const char * toString(std::exception const& val)
    {
        return val.what();
    }

    template<typename TSource> string::string(const TSource & p_source)
    {
        *this = pfc::toString(p_source);
    }
    template<typename TSource> string string::operator+(const TSource & p_item2) const
    {
        return *this + pfc::toString(p_item2);
    }

    //! "String parameter" helper class, to use in function parameters, allowing functions to take any type of string as a parameter (const char*, string_base, string).
    class stringp
    {
    public:
        stringp(const char * ptr) : m_ptr(ptr) {}
        stringp(string const &s) : m_ptr(s.ptr()), m_s(s._content()) {}
        stringp(string_base const &s) : m_ptr(s.get_ptr()) {}
        template<typename TWhat> stringp(const TWhat& in) : m_ptr(in.toString()) {}

        operator const char*() const
        {
            return m_ptr;
        }
        const char * ptr() const
        {
            return m_ptr;
        }
        const char * get_ptr() const
        {
            return m_ptr;
        }
        pfc::string str() const
        {
            return m_s.is_valid() ? pfc::string(m_s) : pfc::string(m_ptr);
        }
        operator pfc::string() const
        {
            return str();
        }
        pfc::string toString() const
        {
            return str();
        }
        t_size length() const
        {
            return m_s.is_valid() ? m_s->length() : strlen(m_ptr);
        }
    private:
        const char * const m_ptr;
        pfc::string::t_data m_s;
    };

    template<typename TList>
    string stringCombineList(const TList & list, stringp separator)
    {
        typename TList::const_iterator iter = list.first();
        string acc;
        if (iter.is_valid())
        {
            acc = *iter;
            for (++iter; iter.is_valid(); ++iter)
            {
                acc = acc + separator + *iter;
            }
        }
        return acc;
    }
}

#endif //_PFC_STRINGNEW_H_
